#include <string.h>

#include "lib.h"
#include "slotserv.h"
#include "slotconn.h"
#include "progserv.h"
#include "progconn.h"
#include "progsql.h"
#include "sltsql.h"
#include "slotsproto.h"
#include "stdncerr.h"
#include "opts.h"
#include "log.h"
#include "slotopts.h"

#include "XMLTranslator.h"

#include "endian.h"

#define APP1		(uint8)'S'
#define APP2		(uint8)'M'

#define GAMEID			18	// Class/Player limits gameId

//
// TODO: use results from tracker-> calls!
//

void
SlotServer::startTracker()
{
	ServerOpts::load();

	tracker = new SlotsSQL(ServerOpts::dbdsn,
		ServerOpts::dblogin, ServerOpts::dbpasswd);

	// CSN-8891
	webServiceCall = new WebServiceCall();
}

void
SlotServer::stopTracker()
{
	delete tracker;
	tracker = 0;

	delete webServiceCall;
	webServiceCall = 0;
}

bool8
SlotServer::initSuccess()
{
	if (tracker && tracker->initOK())
		return Server::initSuccess();
	else
	{
		Log::msgError("Failed to connect to the database.  Aborting.");
		return false;
	}
}

bool8
SlotConnection::parseAnsiCasinoLogin(int32 stdReq, int32* Err)
{
	int32 i;

	if (msgbuf[1]	!= APP1				|| msgbuf[2] != APP2 ||
		msgbuf[3]	!= PROTO_VERSION1	|| msgbuf[4] != PROTO_VERSION2)
	{
		*Err = LOGINERR_BADVERSION;
		DEBUGMSG(("Login: incorrect version: %c%c  ",
				msgbuf[1],msgbuf[2],msgbuf[3],msgbuf[4]));
		return false;
	}

	if (!info)
    {
	    info = new Info();
		if (!info)
		{
			*Err = LOGINERR_TRYLATER;
			return false;
		}
    }

    info->betperline = 0;
    Endian::ReceiveByte(&info->machineID, &msgbuf[5]);
    Endian::ReceiveInt2(&info->machineSubID, &msgbuf[6]);

	i = stdReq;

	char login[PIDLEN + 1];
	memcpy(login, msgbuf + i, PIDLEN);
	login[PIDLEN] = '\0';
	i += PIDLEN;

	strlwr(login);
	info->login = login;

	info->forMoney = (bool8)(msgbuf[i++] != 0);

	// CSN-10426: with this modification, we don't need iClientID sent by the client, so we skip it
	String sClientTypeIntAPI = String::emptyString;
	i++;	// Skip ClientID

	if (xmlTranslator && xmlTranslator->UsingFunPID())
	{
		// CSN-7076: New Authentication (fun play)
		bool8 result;
		tracker->lock();
		result = tracker->getAvailableFunID( info->login );
		tracker->unlock();
		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			delete info;
			info = 0;
			return false;
		}
	}
	else
	{	
		// CSN-7075: New Authentication
		String sToken = String::emptyString;
		i =+ sToken.deserialize(&msgbuf[i]);

		// CSN-8909: Look for clientType in Integration API
		if (!wsCall()->Authenticate(sToken, info->login, sClientTypeIntAPI, m_sCurrency))
		{
			bool8 bAuth = false;
			
			// CSN-12167: connect with no token? In that case, white-listed PID is sent in <Token> field
			#ifdef DEBUGSPIN
			if (ServerOpts::bAllowTesting)
			{
				bAuth = tracker->isFreeFromAuthentication(sToken);					
				if (bAuth)
					info->login = sToken;
			}
			#endif
			
			if (!bAuth)
			{
				*Err = LOGINERR_CONTACTCUSTSERVICE;
				delete info;
				info = 0;
				return false;
			}
		}
	}
	
	if (tracker)
	{
		//int32 result;
		bool8 result;

		tracker->lock();
		//result = tracker->validateLogin(info->login, info->sessionId, info->pid);
		result = tracker->createRTGSession(info->forMoney, info->login, info->sessionId, sClientTypeIntAPI);
		tracker->unlock();
		
		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			delete info;
			info = 0;
			return false;
		}
	}

	info->pid = info->login;
	return true;
}


bool8
SlotConnection::parseUnicodeCasinoLogin(int32 stdReq, int32* Err)
{
	int32 i;

	if (msgbuf[2]	!= APP1				|| msgbuf[3] != APP2 ||
		msgbuf[4]	!= PROTO_VERSION1	|| msgbuf[5] != PROTO_VERSION2)
	{
		*Err = LOGINERR_BADVERSION;
		DEBUGMSG(("Login: incorrect version: %c%c  ",
				msgbuf[2],msgbuf[3],msgbuf[4],msgbuf[5]));
		return false;
	}

	if (!info)
    {
	    info = new Info();
		if (!info)
		{
			*Err = LOGINERR_TRYLATER;
			return false;
		}
    }

	i = stdReq;
    info->betperline = 0;
    Endian::ReceiveByte(&info->machineID, &msgbuf[6]);
    Endian::ReceiveInt2(&info->machineSubID, &msgbuf[7]);

	i += info->login.deserialize(msgbuf + i);

	info->forMoney = (bool8)(msgbuf[i++] != 0);

	// CSN-10426: with this modification, we don't need iClientID sent by the client, so we skip it
	String sClientTypeIntAPI = String::emptyString;
	i++;	// Skip ClientID
	
	if (xmlTranslator && xmlTranslator->UsingFunPID())
	{
		// CSN-7076: New Authentication (fun play)
		bool8 result;
		tracker->lock();
		result = tracker->getAvailableFunID( info->login );
		tracker->unlock();
		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			delete info;
			info = 0;
			return false;
		}
	}
	else
	{
		// CSN-7075: New Authentication
		String sToken = String::emptyString;
		i =+ sToken.deserialize(&msgbuf[i]);

		// CSN-8909: Look for clientType in Integration API
		if (!wsCall()->Authenticate(sToken, info->login, sClientTypeIntAPI, m_sCurrency))
		{
			bool8 bAuth = false;
			
			// CSN-12167: connect with no token? In that case, white-listed PID is sent in <Token> field
			#ifdef DEBUGSPIN
			if (ServerOpts::bAllowTesting)
			{
				bAuth = tracker->isFreeFromAuthentication(sToken);					
				if (bAuth)
					info->login = sToken;
			}
			#endif
			
			if (!bAuth)
			{
				*Err = LOGINERR_CONTACTCUSTSERVICE;
				delete info;
				info = 0;
				return false;
			}
		}
	}
	
	if (tracker)
	{
		//int32 result;
		bool8 result;

		tracker->lock();
		//result = tracker->validateLogin(info->login, info->sessionId, info->pid);
		result = tracker->createRTGSession(info->forMoney, info->login, info->sessionId, sClientTypeIntAPI);
		tracker->unlock();

		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			delete info;
			info = 0;
			return false;
		}
		
	}

	info->pid = info->login;
	return true;
}


void
SlotConnection::removeCasinoSpecific()
{
	delete info;

	info = (Info*)0;
}

int32
SlotConnection::encode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}

int32
SlotConnection::decode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}

bool8
SlotConnection::trackGetPlayerLimits()
{
	int32 result = SlotsSQL::ExecFailed;

	if (tracker && info)
	{
		tracker->lock();
		result = ((SlotsSQL*)tracker)->getPlayerLimits(info->pid, GAMEID, info->machineID, &info->compFactor);
		tracker->unlock();
	}

	return (bool8)(result == SlotsSQL::NoError);
}

bool8
SlotConnection::trackGetBalance()
{
	int32 result = SlotsSQL::ExecFailed;
	int32 throwawayComp;

	if (tracker && info)
	{
		tracker->lock();
		result = tracker->getBalance(ServerOpts::casinoId,
			info->pid, info->forMoney, &info->balance, &throwawayComp);
		tracker->unlock();
	}

	return (bool8)(result == SlotsSQL::NoError);
}

int32
SlotConnection::trackValidateLogin()
{
	int32 result = SlotsSQL::ExecFailed;

	if (tracker && info)
	{
		tracker->lock();
		result = tracker->validateLogin(info->login, info->sessionId, info->pid);
		tracker->unlock();
	}

	return result;
}

int32
SlotConnection::trackValidateSession()
{
	int32 result = SlotsSQL::ExecFailed;

	if (tracker && info)
	{
		tracker->lock(); 
		result = tracker->validateSession(info->pid, info->sessionId);
		tracker->unlock();
	}

	return result;
}

bool8 SlotConnection::getForRealFlag()
{
    if (info && info->forMoney)
        return true;
    else 
        return false;
}

bool8 SlotConnection::getLocale( localCurrency* lc, int32* chipValues, int32 numChipValues )
{
	bool8 ok = false;
    if (tracker)
    {
		tracker->lock();
		ok = ( TrackSQL::NoError == tracker->getLocale( lc, chipValues ) );
		tracker->unlock();
	}
	return ok;
}

void SlotConnection::trackLogoutSession()
{
    if (tracker && info && !info->login.isEmpty())
    {
        tracker->lock();
		tracker->logout(ServerOpts::casinoId, info->login, info->forMoney, String::emptyString, info->sessionId);
        tracker->unlock();
    }
}